package main

import (
	"bytes"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"strings"
	"unicode"
)

// 输入原始表名,返回去除表前缀的表名
// 第一个返回值:表前缀，第二个返回值:去除表前缀的表名
func (m *schema) Table(s string) (string, string) {
	var (
		prefix string
		name   string
	)
	if strings.Contains(s, "_") {
		split := strings.Split(s, "_")
		prefix = split[0]
		str := split[1:]
		for _, val := range str {
			vv := []rune(val) // 后文有介绍
			for i := 0; i < len(vv); i++ {
				if i == 0 {
					if vv[i] >= 97 && vv[i] <= 122 { // 后文有介绍
						vv[i] -= 32 // string的码表相差32位
						name += string(vv[i])
					}
				} else {
					name += string(vv[i])
				}
			}
		}
	} else {
		return "", s
	}
	return prefix, name
}

// 蛇形转驼峰 xx_yy ==> XxYx  xx_y_y ==> XxYY
func (m *schema) Camel2String(s string) string {
	res := make([]byte, 0, len(s))
	j := false
	k := false
	num := len(s) - 1
	for i := 0; i <= num; i++ {
		d := s[i]
		if k == false && d >= 'A' && d <= 'Z' {
			k = true
		}
		if d >= 'a' && d <= 'z' && (j || k == false) {
			d = d - 32
			j = false
			k = true
		}
		if k && d == '_' && num > i && s[i+1] >= 'a' && s[i+1] <= 'z' {
			j = true
			continue
		}
		res = append(res, d)
	}
	return string(res[:])
}

// 返回去除表前缀的大驼峰表名
func (m *schema) CamelTable(s string) string {
	_, tab := m.Table(s)
	return m.Camel2String(tab)
}

// Capitalize 将字符串转换成驼峰格式，字符首字母大写。
func (m *schema) Capitalize(s string) string {
	var upperStr string
	if strings.Contains(s, "_") {
		chars := strings.Split(s, "_")
		for _, val := range chars {
			vv := []rune(val) // 后文有介绍
			for i := 0; i < len(vv); i++ {
				if i == 0 {
					if vv[i] >= 97 && vv[i] <= 122 { // 后文有介绍
						vv[i] -= 32 // string的码表相差32位
						upperStr += string(vv[i])
					}
				} else {
					upperStr += string(vv[i])
				}
			}
		}
	} else {
		upperStr = m.Up(s)
	}
	return upperStr
}

func (m *schema) Up(str string) string {
	var upperStr string
	vv := []rune(str) // 后文有介绍
	for i := 0; i < len(vv); i++ {
		if i == 0 {
			if vv[i] >= 97 && vv[i] <= 122 { // 后文有介绍
				vv[i] -= 32 // string的码表相差32位
				upperStr += string(vv[i])
			} else {
				fmt.Println("Not begins with lowercase letter,")
				return str
			}
		} else {
			upperStr += string(vv[i])
		}
	}
	return upperStr
}

// 蛇形字符串转小驼峰字符串。
func (m *schema) Snake2Camel(name string) string {
	name = strings.Replace(name, "_", " ", -1)
	name = strings.Title(name)
	str := strings.Replace(name, " ", "", -1)
	for i, v := range str {
		return string(unicode.ToLower(v)) + str[i+1:]
	}
	return ""
}

//获取操作系统
func getOs() int {
	switch runtime.GOOS {
	case "darwin":
		return darwin
	case "windows":
		return window
	case "linux":
		return linux
	default:
		return unknown
	}
}

//清屏
func clean() {
	switch getOs() {
	case darwin, linux:
		cm := exec.Command("clear")
		cm.Stdout = os.Stdout
		cm.Run()
	case window:
		cm := exec.Command("cmd", "/c", "cls")
		cm.Stdout = os.Stdout
		cm.Run()
	}
}

//GetRootDir 获取执行路径
func (m *schema) getExeRootDir() string {
	// 文件不存在获取执行路径
	file, err := filepath.Abs(filepath.Dir(os.Args[0]))
	if err != nil {
		file = fmt.Sprintf(".%s", string(os.PathSeparator))
	} else {
		file = fmt.Sprintf("%s%s", file, string(os.PathSeparator))
	}
	return file
}

// 判断是文件还是目录，根据decideDir为true表示判断是否为目录；否则判断是否为文件
func (m *schema) isFileOrDir(filename string, decideDir bool) bool {
	fileInfo, err := os.Stat(filename)
	if err != nil {
		return false
	}
	isDir := fileInfo.IsDir()
	if decideDir {
		return isDir
	}
	return !isDir
}

// 判断给定文件名是否是一个目录
// 如果文件名存在并且为目录则返回 true。如果 filename 是一个相对路径，则按照当前工作目录检查其相对路径。
func (m *schema) isDir(filename string) bool {
	return m.isFileOrDir(filename, true)
}

//判断文件 或 目录是否存在
func (m *schema) isDirOrFileExist(path string) bool {
	_, err := os.Stat(path)
	return err == nil || os.IsExist(err)
}

//创建目录
func (m *schema) CreateDir(path string) bool {
	if m.isDirOrFileExist(path) == false {
		err := os.MkdirAll(path, os.ModePerm)
		if err != nil {
			return false
		}
	}
	return true
}

//生成目录,不存在则创建,存在则加/
func (m *schema) GenerateDir(path string) (string, error) {
	if len(path) == 0 {
		return "", errors.New("directory is null")
	}
	last := path[len(path)-1:]
	if !strings.EqualFold(last, string(os.PathSeparator)) {
		path = path + string(os.PathSeparator)
	}
	if !m.isDir(path) {
		if m.CreateDir(path) {
			return path, nil
		}
		return "", errors.New(path + "Failed to create or insufficient permissions")
	}
	return path, nil
}

//创建目录
func (m *schema) createDir(path string) string {
	_, err := os.Stat(path)
	if err != nil && os.IsNotExist(err) {
		err = os.MkdirAll(path, os.ModePerm)
		if err != nil {
			log.Fatalf("Directory created failed>>%s\n", path)
			return ""
		}
		fmt.Printf("Directory created success:%s\n", path)
	}
	return path
}

//将分隔_拆掉,全大写
func (m *schema) ToUpper(s string) string {
	return strings.ToUpper(s)
}

//追加写文件
func (m *schema) writeFileAppend(filename string, data string) (err error) {
	var file *os.File
	_, err = os.Stat(filename)
	if err != nil && os.IsNotExist(err) {
		file, err = os.Create(filename)
		if err != nil {
			return
		}
	} else {
		file, err = os.OpenFile(filename, os.O_CREATE|os.O_APPEND|os.O_RDWR, 0666)
	}
	defer file.Close()
	_, err = io.WriteString(file, data)
	if err != nil {
		return
	}
	fmt.Printf("Generate success:%s\n", filename)
	return
}

func (m *schema) execCommand(name string, args ...string) bool {
	cm := exec.Command(name, args...)
	_, err := cm.Output()
	if err != nil {
		return false
	}
	return true
}

//FMT 格式代码
func (m *schema) gofmt(path string) bool {
	if m.isDirOrFileExist(path) {
		if !m.execCommand("goimports", "-l", "-w", path) {
			if !m.execCommand("gofmt", "-l", "-w", path) {
				return m.execCommand("go", "fmt", path)
			}
		}
		return true
	}
	return false
}

//读取文件内容
func (m *schema) readFile(filename string) string {
	res, err := ioutil.ReadFile(filename)
	if err != nil {
		return ""
	}
	return string(res)
}

//检查某字符是否存在文件里
func (m *schema) checkFileContainsChar(filename, s string) bool {
	res := m.readFile(filename)
	if len(res) > 0 {
		return strings.LastIndex(res, s) > 0
	}
	return false
}

//写文件
func (m *schema) writeFile(filename string, data string) (count int, err error) {
	var file *os.File
	if m.isDirOrFileExist(filename) == false {
		file, err = os.Create(filename)
		if err != nil {
			return
		}
	} else {
		file, err = os.OpenFile(filename, os.O_CREATE|os.O_WRONLY, 0666)
	}
	defer file.Close()
	count, err = io.WriteString(file, data)
	if err != nil {
		return
	}
	return
}

//添加注释
func (m *schema) addToComment(s string, suff string) string {
	if strings.EqualFold(s, "") {
		return ""
	}
	return "// " + s + suff
}

//拼接特殊字符串
//  即拼接这个: `json:"username"` 或者 `form:"username"`
//  username 是数据库表中的列名
func (m *schema) formatField(field string, tagKeys []string) string {
	if len(tagKeys) == 0 {
		return ""
	}
	buf := bytes.Buffer{}
	for key := range tagKeys {
		buf.WriteString(fmt.Sprintf(`%s:"%s" `, tagKeys[key], field))
	}
	return "`" + strings.TrimRight(buf.String(), " ") + "`"
}
